#include <gpiod.h> // 包含libgpiod库的头文件
#include <stdio.h> // 包含标准输入输出库
#include <unistd.h> // 包含UNIX标准库，用于usleep函数
#include <stdlib.h> // 包含标准库，用于malloc和free函数
#include "record.h" // 包含自定义的录音头文件

#define GPIO_LINE 9 // 定义GPIO线的编号，这里是PF9

// 初始化GPIO芯片
struct gpiod_chip *initialize_gpio_chip(const char *chip_label) {
    struct gpiod_chip *chip = gpiod_chip_open_by_label(chip_label);
    if (!chip) {
        perror("打开GPIO芯片失败"); // 如果打开失败，打印错误信息
    }
    return chip;
}

// 初始化GPIO线
struct gpiod_line *initialize_gpio_line(struct gpiod_chip *chip, int line_num) {
    struct gpiod_line *line = gpiod_chip_get_line(chip, line_num);
    if (!line) {
        perror("获取GPIO线失败"); // 如果获取失败，打印错误信息
        gpiod_chip_close(chip); // 关闭GPIO芯片
    }
    return line;
}

// 请求将GPIO线设置为输入模式
int request_gpio_input(struct gpiod_line *line, const char *consumer) {
    if (gpiod_line_request_input(line, consumer)) {
        perror("请求将GPIO线设置为输入模式失败"); // 如果请求失败，打印错误信息
        return -1;
    }
    return 0;
}

// 处理按键按下事件
void handle_key_pressed(snd_pcm_t **capture, snd_pcm_uframes_t *period, char **buffer, FILE **fp) {
    printf("key pressed\n");
    *capture = record_open("hw:0,1", SND_PCM_FORMAT_S16_LE, 2, 44100, period); // 打开音频采集设备
    if (!*capture) {
        return;
    }

    *buffer = malloc(snd_pcm_frames_to_bytes(*capture, *period)); // 分配缓冲区
    if (!*buffer) {
        perror("malloc"); // 如果分配失败，打印错误信息
        record_close(*capture);
        *capture = NULL;
        return;
    }

    *fp = fopen("output.pcm", "wb"); // 打开录音文件
    if (!*fp) {
        perror("Error opening output file"); // 如果打开失败，打印错误信息
        free(*buffer); // 释放缓冲区
        *buffer = NULL;
        record_close(*capture); // 关闭PCM设备
        *capture = NULL;
    }
}

// 处理按键释放事件
void handle_key_released(snd_pcm_t **capture, char **buffer, FILE **fp) {
    printf("key released\n");
    if (*fp) {
        fclose(*fp); // 关闭录音文件
        *fp = NULL;
    }
    if (*buffer) {
        free(*buffer); // 释放缓冲区
        *buffer = NULL;
    }
    record_close(*capture); // 关闭PCM设备
    *capture = NULL;
}

// 录制音频
void record_audio(snd_pcm_t *capture, char *buffer, snd_pcm_uframes_t period, FILE *fp) {
    snd_pcm_sframes_t frames = snd_pcm_readi(capture, buffer, period); // 从PCM设备读取数据
    if (frames < 0) {
        fprintf(stderr, "Error from read: %s\n", snd_strerror(frames)); // 如果读取失败，打印错误信息
        snd_pcm_recover(capture, frames, 0); // 尝试恢复PCM设备
    }

    fwrite(buffer, snd_pcm_frames_to_bytes(capture, frames), 1, fp); // 将读取的数据写入文件
}

// 检查按键状态并处理
void check_key_state(struct gpiod_line *line, int *last_value, snd_pcm_t **capture, snd_pcm_uframes_t *period, char **buffer, FILE **fp) {
    int value = gpiod_line_get_value(line); // 获取当前的GPIO线值

    if (value != *last_value) { // 如果当前值与上一次的值不同，说明按键状态发生了变化
        if (value == 0) { // 如果当前值为0，表示按键被按下
            handle_key_pressed(capture, period, buffer, fp);
        } else { // 如果当前值为1，表示按键被释放
            handle_key_released(capture, buffer, fp);
        }
        *last_value = value; // 更新上一次的值为当前值
    }

    if (value == 0 && *capture) { // 如果按键按下并且音频采集设备已打开
        record_audio(*capture, *buffer, *period, *fp); // 录制音频
    }
}

int main(void) {
    struct gpiod_chip *chip; // 定义指向GPIO芯片的指针
    struct gpiod_line *line; // 定义指向GPIO线的指针
    int last_value; // 定义上一次的值，用于检测状态变化
    snd_pcm_uframes_t period; // 周期大小
    snd_pcm_t* capture = NULL; // 音频采集设备
    char* buffer = NULL; // 存放音频数据的缓冲区
    FILE* fp = NULL; // 录音文件

    // 初始化GPIO芯片
    chip = initialize_gpio_chip("GPIOF");
    if (!chip) return 1;

    // 初始化GPIO线
    line = initialize_gpio_line(chip, GPIO_LINE);
    if (!line) return 1;

    // 请求将GPIO线设置为输入模式
    if (request_gpio_input(line, "key1") < 0) {
        gpiod_chip_close(chip);
        return 1;
    }

    // 获取初始的GPIO线值
    last_value = gpiod_line_get_value(line);

    // 无限循环检测GPIO线值的变化
    while (1) {
        check_key_state(line, &last_value, &capture, &period, &buffer, &fp); // 检查按键状态并处理
        // usleep(100000); // 延时100毫秒，防止检测过于频繁
    }

    // 释放GPIO线资源
    gpiod_line_release(line);
    // 关闭GPIO芯片
    gpiod_chip_close(chip);
    return 0;
}
